﻿
// GreedySnakeDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "GreedySnake.h"
#include "GreedySnakeDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CGreedySnakeDlg 对话框





CGreedySnakeDlg::CGreedySnakeDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_GREEDYSNAKE_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CGreedySnakeDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_EDIT1, m_difficulty);
	DDX_Control(pDX, IDC_EDIT2, m_score);
	DDX_Control(pDX, IDC_EDIT3, m_score1);
	DDX_Control(pDX, IDC_EDIT4, m_score2);
	DDX_Control(pDX, IDC_STATIC_Single, m_static_single);
	DDX_Control(pDX, IDC_STATIC_Double, m_static_double);
	DDX_Control(pDX, IDC_GAMEAREA, m_gamearea);
}

BEGIN_MESSAGE_MAP(CGreedySnakeDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_EN_CHANGE(IDC_EDIT2, &CGreedySnakeDlg::OnEnChangeEdit2)
	ON_COMMAND(ID_32771, &CGreedySnakeDlg::OnStart)
	ON_WM_TIMER()
	ON_COMMAND(ID_32772, &CGreedySnakeDlg::OnPause)
	ON_COMMAND(ID_32773, &CGreedySnakeDlg::OnContinue)
	ON_COMMAND(ID_Menu, &CGreedySnakeDlg::OnAuto)
	ON_COMMAND(ID_32777, &CGreedySnakeDlg::OnExit)
	ON_COMMAND(ID_32775, &CGreedySnakeDlg::OnSingle)
	ON_UPDATE_COMMAND_UI(ID_32775, &CGreedySnakeDlg::OnSingleMenu)
	ON_WM_INITMENUPOPUP()
	ON_COMMAND(ID_Menu32776, &CGreedySnakeDlg::OnDouble)
	ON_UPDATE_COMMAND_UI(ID_Menu32776, &CGreedySnakeDlg::OnDoubleMenu)
	ON_STN_CLICKED(IDC_STATIC_Double, &CGreedySnakeDlg::OnStnClickedStaticDouble)
	ON_STN_CLICKED(IDC_STATIC_Single, &CGreedySnakeDlg::OnStnClickedStaticSingle)
	ON_WM_CTLCOLOR()
END_MESSAGE_MAP()


// CGreedySnakeDlg 消息处理程序

BOOL CGreedySnakeDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时，框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码

	//设置游戏区域控件的大小为800*800像素
	CWnd* pWnd;
	pWnd = GetDlgItem(IDC_GAMEAREA);
	pWnd->SetWindowPos(NULL, 0, 0, 800, 800, SWP_NOZORDER | SWP_NOMOVE);
	

	//初始化分数，难度
	score = 0;
	difficulty = 1;

	//分别转化为字符串形式，插入到编辑框中
	CString str;
	str.Format(TEXT("%d"), difficulty);
	m_difficulty.SetWindowTextW(str);

	str.Format(TEXT("%d"), score);
	m_score.SetWindowTextW(str);

	//初始化不是自动游戏
	isAuto = false;
	//初始化单人游戏
	isSingle = true;

	//设置Static控件的字体
	m_Font.CreatePointFont(150, _T("宋体"), NULL);//参数：字体大小，样式，DC
	m_static_single.SetFont(&m_Font, true);
	m_static_double.SetFont(&m_Font, true);
	////设置游戏区域背景透明
	//m_gamearea.ModifyStyle(0, WS_EX_TRANSPARENT);

	return TRUE;  // 除非将焦点设置到控件，否则返回 TRUE
}

void CGreedySnakeDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮，则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序，
//  这将由框架自动完成。

void CGreedySnakeDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		/*CDialogEx::OnPaint();*/

		//添加代码对话框背景贴图
		CPaintDC   dc(this);
		CRect   rect;
		GetClientRect(&rect);    //获取对话框长宽      
		CDC   dcBmp;             //定义并创建一个内存设备环境
		dcBmp.CreateCompatibleDC(&dc);             //创建兼容性DC
		CBitmap   bmpBackground;
		bmpBackground.LoadBitmap(IDB_BITMAP1);    //载入资源中图片
		BITMAP   m_bitmap;                         //图片变量               
		bmpBackground.GetBitmap(&m_bitmap);       //将图片载入位图中
		//将位图选入临时内存设备环境
		CBitmap* pbmpOld = dcBmp.SelectObject(&bmpBackground);
		//调用函数显示图片StretchBlt显示形状可变
		dc.StretchBlt(0, 0, rect.Width(), rect.Height(), &dcBmp, 0, 0, m_bitmap.bmWidth, m_bitmap.bmHeight, SRCCOPY);
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CGreedySnakeDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void CGreedySnakeDlg::OnEnChangeEdit2()
{
	// TODO:  如果该控件是 RICHEDIT 控件，它将不
	// 发送此通知，除非重写 CDialogEx::OnInitDialog()
	// 函数并调用 CRichEditCtrl().SetEventMask()，
	// 同时将 ENM_CHANGE 标志“或”运算到掩码中。

	// TODO:  在此添加控件通知处理程序代码
}

//开始
void CGreedySnakeDlg::OnStart()
{
	// TODO: 在此添加命令处理程序代码
	snake1.Init(8);

	//每次开始时不自动游戏
	isAuto = false;

	//重置分数和难度
	score = 0;
	difficulty = 1;

	//初始化两个玩家的分数
	score1 = 0;
	score2 = 0;
	//单人模式
	if (isSingle)
	{	

		CString str;
		str.Format(TEXT("%d"), difficulty);
		m_difficulty.SetWindowTextW(str);

		str.Format(TEXT("%d"), score);
		m_score.SetWindowTextW(str);
	}
	//双人模式
	else if (!isSingle)
	{
		snake2.Init(12);

		CString str;
		str.Format(TEXT("%d"), score1);
		m_score1.SetWindowTextW(str);

		str.Format(TEXT("%d"), score2);
		m_score2.SetWindowTextW(str);
	}

	// 启动ID为1的定时器，定时时间为1000毫秒
	SetTimer(1, 500, NULL);

	//产生食物
	if (isSingle)
	{
		Food = GenerateFood(snake1.GetBody(), 20, 20);
	}
	else
	{
		Food = GenerateFood(snake1.GetBody(), snake2.GetBody(), 20, 20);
	}

	//DrawSnake();

}

//定时器
void CGreedySnakeDlg::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	//判断是否进行自动游戏
	if (isAuto)
	{
		snake1.AutoMoveToFood(Food);
	}

	//移动蛇1
	snake1.Move(Food);

	//当前是双人模式时，移动蛇2
	if (!isSingle)
	{
		snake2.Move(Food);
	}

	//当是否撞墙或者咬到自己时，游戏结束
	if (snake1.CheckCollision())
	{
		KillTimer(1);
		if (isSingle)
		{
			MessageBox(TEXT("游戏结束"), TEXT("提示"));
		}
		else
		{
			MessageBox(TEXT("游戏结束，玩家2获胜！"), TEXT("提示"));
		}
		return;
	}
	if (!isSingle)    //双人模式时也要判断蛇2
	{
		if (snake2.CheckCollision())
		{
			KillTimer(1);
			MessageBox(TEXT("游戏结束，玩家1获胜！"), TEXT("提示"));
			return;
		}
	}

	//当吃到食物时，则产生新的食物，分数和难度也同时增加
	if (snake1.ateFood)
	{
		//当是单人模式
		if (isSingle)    
		{
			Food = GenerateFood(snake1.GetBody(), 20, 20);
			score++;
			//分数每增加3次，难度增加1即移动速度变快
			if (score % 3 == 0)
			{
				//当难度达到15时就不再增加速度
				if (difficulty < 15)
				{
					difficulty++;
					SetTimer(1, 200 - difficulty * 7, NULL);
				}
			}
			//重新显示分数和难度
			CString str;
			str.Format(TEXT("%d"), difficulty);
			m_difficulty.SetWindowTextW(str);

			str.Format(TEXT("%d"), score);
			m_score.SetWindowTextW(str);
		}
		else    //如果是双人模式
		{
			Food = GenerateFood(snake1.GetBody(), snake2.GetBody(), 20, 20);
			score1++;
			if (score1 > 24)
			{
				KillTimer(1);
				MessageBox(TEXT("游戏结束，玩家1达成25分，获胜！"), TEXT("提示"));
				return;
			}
			//重新显示分数1和分数2
			CString str;    
			str.Format(TEXT("%d"), score1);
			m_score1.SetWindowTextW(str);

			str.Format(TEXT("%d"), score2);
			m_score2.SetWindowTextW(str);

		}
		snake1.ateFood = false;	

	}
	//当是双人模式且蛇2吃到食物
	if (!isSingle && snake2.ateFood)
	{
		Food = GenerateFood(snake1.GetBody(), snake2.GetBody(), 20, 20);
		snake2.ateFood = false;

		score2++;
		if (score2 > 24)
		{
			KillTimer(1);
			MessageBox(TEXT("游戏结束，玩家2达成25分，获胜！"), TEXT("提示"));
			return;
		}
		//重新显示分数1和分数2
		CString str;
		str.Format(TEXT("%d"), score1);
		m_score1.SetWindowTextW(str);

		str.Format(TEXT("%d"), score2);
		m_score2.SetWindowTextW(str);

	}
	DrawSnake();

	CDialogEx::OnTimer(nIDEvent);
}

//绘制蛇
void CGreedySnakeDlg::DrawSnake()
{
	CWnd* pWnd = GetDlgItem(IDC_GAMEAREA);    //获得游戏区域控件的指针
	pWnd->Invalidate();    //使游戏区域控件无效
	pWnd->UpdateWindow();    //强制刷新游戏区域控件
	CDC* pDC = pWnd->GetDC();

	snake1.Draw(pDC, RGB(34, 139, 34));
	snake2.Draw(pDC, RGB(30, 144, 255));

	//创建红色画刷绘制食物
	CBrush red_brush(RGB(255, 0, 0));
	//计算食物绘制的位置
	int left = Food.x * BodySize;
	int top = Food.y * BodySize;
	int right = left + BodySize;
	int bottom = top + BodySize;
	//绘制食物
	pDC->SelectObject(&red_brush);
	pDC->Rectangle(left, top, right, bottom);
}

//产生食物（单人模式）
CPoint CGreedySnakeDlg::GenerateFood(vector<CPoint>& snakeBody, int maxX, int maxY)
{
	//随机生成食物
	random_device rd;
	mt19937 gen(rd());
	uniform_int_distribution<int> xDist(0, maxX - 1);    //食物x的坐标范围
	uniform_int_distribution<int> yDist(0, maxY - 1);    //食物y的坐标范围
	
	int foodX, foodY;
	do
	{
		//随机生成食物坐标
		foodX = xDist(gen);
		foodY = yDist(gen);
	}
	//检查生成的食物是否在蛇身体上，如果是，重新生成
	while (std::any_of(snakeBody.begin(), snakeBody.end(), [&](const CPoint& p)
		{
			return p.x == foodX && p.y == foodY;
		}));
	return CPoint(foodX, foodY);
}

//产生食物（双人模式）
CPoint CGreedySnakeDlg::GenerateFood(vector<CPoint>& snakeBody1, vector<CPoint>& snakeBody2, int maxX, int maxY)
{
	//随机生成食物
	random_device rd;
	mt19937 gen(rd());
	uniform_int_distribution<int> xDist(0, maxX - 1);    //食物x的坐标范围
	uniform_int_distribution<int> yDist(0, maxY - 1);    //食物y的坐标范围

	int foodX, foodY;
	do
	{
		//随机生成食物坐标
		foodX = xDist(gen);
		foodY = yDist(gen);
	}
	//检查生成的食物是否在蛇身体上，如果是，重新生成
	while (std::
		any_of(snakeBody1.begin(), snakeBody1.end(), [&](const CPoint& p)
		{
			return p.x == foodX && p.y == foodY;
		}) || 
		any_of(snakeBody2.begin(), snakeBody2.end(), [&](const CPoint& p)
		{
				return p.x == foodX && p.y == foodY;
			}
			));
	return CPoint(foodX, foodY);
}


//方向键控制蛇的方向
BOOL CGreedySnakeDlg::PreTranslateMessage(MSG* pMsg)
{
	// TODO: 在此添加专用代码和/或调用基类
	//禁用方向键的默认行为，并用键盘控制方向
	if (pMsg->message == WM_KEYDOWN)
	{
		switch (pMsg->wParam)
		{
		case VK_UP:
			snake1.SetDirection(Direction::UP);
			break;
		case VK_DOWN:
			snake1.SetDirection(Direction::DOWN);
			break;
		case VK_LEFT:
			snake1.SetDirection(Direction::LEFT);
			break;
		case VK_RIGHT:
			snake1.SetDirection(Direction::RIGHT);
			break;
		case 65:    //传VK_A的十进制数，左走
			snake2.SetDirection(Direction::LEFT);
			break;
		case 68:    //D，右走
			snake2.SetDirection(Direction::RIGHT);
			break;
		case 87:    //W，上走
			snake2.SetDirection(Direction::UP);
			break;
		case 83:   //S，下走
			snake2.SetDirection(Direction::DOWN);
			break;
		}
		
	}
	return CDialogEx::PreTranslateMessage(pMsg);
}

//暂停
void CGreedySnakeDlg::OnPause()
{
	// TODO: 在此添加命令处理程序代码
	//关闭定时器
	KillTimer(1);
}

//继续
void CGreedySnakeDlg::OnContinue()
{
	// TODO: 在此添加命令处理程序代码
	//重新打开定时器
	SetTimer(1, 200 - difficulty * 7, NULL);
}

//自动游戏
void CGreedySnakeDlg::OnAuto()
{
	// TODO: 在此添加命令处理程序代码
	isAuto = true;

}

//退出
void CGreedySnakeDlg::OnExit()
{
	// TODO: 在此添加命令处理程序代码
	//点击退出时关闭窗口
	exit(0);
}

//单人游戏
void CGreedySnakeDlg::OnSingle()
{
	// TODO: 在此添加命令处理程序代码
	isSingle = true;
}


void CGreedySnakeDlg::OnSingleMenu(CCmdUI* pCmdUI)
{
	// TODO: 在此添加命令更新用户界面处理程序代码
	pCmdUI->SetCheck(isSingle);
}


void CGreedySnakeDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
	CDialogEx::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);

	// TODO: 在此处添加消息处理程序代码
	if (!bSysMenu && pPopupMenu)
	{
		CCmdUI cmdUI;
		cmdUI.m_pOther = NULL;
		cmdUI.m_pMenu = pPopupMenu;
		cmdUI.m_pSubMenu = NULL;

		UINT count = pPopupMenu->GetMenuItemCount();
		cmdUI.m_nIndexMax = count;
		for (UINT i = 0; i < count; i++)
		{
			UINT nID = pPopupMenu->GetMenuItemID(i);
			if (-1 == nID || 0 == nID)
			{
				continue;
			}
			cmdUI.m_nID = nID;
			cmdUI.m_nIndex = i;
			cmdUI.DoUpdate(this, FALSE);
		}
	}
}


void CGreedySnakeDlg::OnDouble()
{
	// TODO: 在此添加命令处理程序代码
	isSingle = false;
}


void CGreedySnakeDlg::OnDoubleMenu(CCmdUI* pCmdUI)
{
	// TODO: 在此添加命令更新用户界面处理程序代码
	pCmdUI->SetCheck(!isSingle);
}


void CGreedySnakeDlg::OnStnClickedStaticDouble()
{
	// TODO: 在此添加控件通知处理程序代码
}


void CGreedySnakeDlg::OnStnClickedStaticSingle()
{
	// TODO: 在此添加控件通知处理程序代码
}


HBRUSH CGreedySnakeDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

	// TODO:  在此更改 DC 的任何特性
	
	//设置static控件字体
	if (nCtlColor == CTLCOLOR_STATIC 
		&& pWnd->GetDlgCtrlID() != IDC_GAMEAREA 		                            
		&& pWnd->GetDlgCtrlID() != IDC_EDIT1
		&& pWnd->GetDlgCtrlID() != IDC_EDIT2 
		&& pWnd->GetDlgCtrlID() != IDC_EDIT3 
		&& pWnd->GetDlgCtrlID() != IDC_EDIT4)
	{
		pDC->SetTextColor(RGB(255, 255, 0));    //字体颜色设置为黄色
		pDC->SetBkMode(TRANSPARENT);    //设置控件透明
		return (HBRUSH)GetStockObject(NULL_BRUSH);    //返回一个透明画刷
	}
	//if (pWnd->GetDlgCtrlID() == IDC_GAMEAREA)
	//{
	//	pDC->SetBkMode(TRANSPARENT);    //设置控件透明
	//}
	//设置edit控件背景黑色
	//if (nCtlColor == CTLCOLOR_EDIT)
	//{
	//	pDC->SetBkColor(RGB(0,0,0));    //设置控件背景为黑色
	//	return (HBRUSH)GetStockObject(BLACK_BRUSH);    //返回一个黑色画刷
	//}
	

	// TODO:  如果默认的不是所需画笔，则返回另一个画笔
	return hbr;
}
